package timerwheel

import (
	"fmt"
	"time"
)

// 设置开始时间
func TimerSetNow(n time.Time) option {
	return func(t *Timer) {
		t.now = n.UnixMilli()
	}
}

// 设置最小刻度（毫秒）
func TimerSetAccuracy(n int64) option {
	return func(t *Timer) {
		t.TimerWheelAccuracy = n
	}
}

type option func(t *Timer)

/*
时间桶按毫秒运行
*/
type Timer struct {
	TimerWheelAccuracy int64 //最小时间刻度（毫秒）

	isStartTimer bool                  //是否开始定时器
	timerTaskID  uint64                //定时任务起始ID
	timerTasks   map[uint64]*taskModel //定时任务集合
	bucketList   map[int64]*bucket     //定时器的桶

	now     int64 //当前时间
	initVal int64 //系统最开始的值，用与减小Key的值
	nowKey  int64 //当前时间桶Key
	lastKey int64 //上次调用桶Key

}

func NewTimer(opts ...option) *Timer {
	result := new(Timer)
	result.TimerWheelAccuracy = 100
	result.timerTaskID = 0
	result.isStartTimer = false
	result.timerTasks = make(map[uint64]*taskModel)
	result.bucketList = make(map[int64]*bucket)
	result.now = time.Now().UnixMilli()

	for i := range opts {
		opts[i](result)
	}

	result.initVal = result.now
	result.nowKey = 0
	result.lastKey = result.nowKey
	return result
}

func (this *Timer) Start() {
	this.isStartTimer = true

}

// 重新启动，需要一个时间,(毫秒)
func (this *Timer) ReStart(t int64) {
	/*
	   需要把原有的桶都去掉；
	   对任务的时间重新计算；
	   然后重新放进桶里面；
	*/
	for _, bk := range this.bucketList {
		bk.init()
		PoolBucket.Put(bk)
	}
	this.bucketList = make(map[int64]*bucket)
	//最后一次Tick的时间刻度
	lastnow := this.initVal + this.lastKey*this.TimerWheelAccuracy

	//设置新的开始时间
	this.now = t
	this.initVal = this.now
	this.nowKey = 0
	this.lastKey = this.nowKey
	for _, taskmd := range this.timerTasks {
		taskmd.tickTimer = taskmd.tickTimer - lastnow + this.initVal + this.lastKey*this.TimerWheelAccuracy
		taskmd.timerKey = this.getUnixKey(taskmd.tickTimer)
		bk := this.getBucket(taskmd.timerKey)
		bk.AddTask(taskmd)
	}
	this.isStartTimer = true
}

func (this *Timer) Stop() {
	this.isStartTimer = false
}

func (this *Timer) Len() int {
	return len(this.timerTasks)
}

// 获取定时器当前Tick时间
func (this *Timer) GetTickTime() time.Time {
	// "+1-this.TimerWheelAccuracy" 修正
	// (t.UnixMilli() - this.initVal + this.TimerWheelAccuracy - 1) / this.TimerWheelAccuracy =  re
	tickmill := this.lastKey*this.TimerWheelAccuracy + this.initVal
	return time.UnixMilli(tickmill)
}

func (this *Timer) OnTick(t time.Time) {
	if !this.isStartTimer {
		return
	}
	this.now = t.UnixMilli()
	nkey := this.getNowKey(t)
	this.nowKey = nkey
	for i := this.lastKey; i < nkey; i++ {
		this.lastKey = i
		bk, ok := this.bucketList[i]
		if !ok {
			continue
		}
		for bk.Root != nil {
			this.callback(bk)
		}
		bk.Prev = nil
		delete(this.bucketList, i)
		bk.init()
		PoolBucket.Put(bk)
	}
}

func (this *Timer) callback(bk *bucket) {
	defer func() {
		tmp := bk.Root
		bk.Root = bk.Root.Next
		tmp.Next = nil
		if !this.loopTask(tmp) {
			delete(this.timerTasks, tmp.id)
			tmp.init()
			PoolListE.Put(tmp)
		}
	}()
	if !bk.Root.isDelete {
		bk.Root.callback(bk.Root.params...)
	}

}

//----------------------------------------------------

// 生成定时任务ID
func (this *Timer) genTimerTaskID() uint64 {
	this.timerTaskID++
	return this.timerTaskID
}

// 获取运行时间所在的桶Key
func (this *Timer) getNowKey(t time.Time) int64 {
	return (t.UnixMilli() - this.initVal + this.TimerWheelAccuracy - 1) / this.TimerWheelAccuracy
}

func (this *Timer) getUnixKey(t int64) int64 {
	return (t - this.initVal + this.TimerWheelAccuracy - 1) / this.TimerWheelAccuracy
}

// 添加新任务
func (this *Timer) addTask(delayTime int64, intervalTime int64, loopCount int, typ TimerTaskType, callback func(...interface{}), endCallback func(), params ...interface{}) uint64 {
	//新建定时任务
	taskmd := PoolListE.Get().(*taskModel)
	taskmd.id = this.genTimerTaskID()
	taskmd.typ = typ
	taskmd.callback = callback
	taskmd.endCallback = endCallback
	taskmd.params = params
	taskmd.loopTimer = intervalTime
	taskmd.loopCount = loopCount
	taskmd.isDelete = false
	// 如果有延迟时间第一次按延迟时间算，如果没有延迟时间按循环时间算
	if delayTime == 0 {
		taskmd.tickTimer = this.initVal + this.lastKey*this.TimerWheelAccuracy + intervalTime
	} else {
		taskmd.tickTimer = this.initVal + this.lastKey*this.TimerWheelAccuracy + delayTime
	}
	taskmd.timerKey = this.getUnixKey(taskmd.tickTimer)
	this.timerTasks[taskmd.id] = taskmd
	bk := this.getBucket(taskmd.timerKey)
	bk.AddTask(taskmd)
	return taskmd.id
}

func (this *Timer) loopTask(task *taskModel) bool {
	if task.isDelete {
		return false
	}
	if task.typ == TimerTask_loop {
		if task.loopCount == 0 {
			return false
		} else if task.loopCount != -1 {
			task.loopCount--
		}
		task.tickTimer = task.tickTimer + task.loopTimer
		task.timerKey = this.getUnixKey(task.tickTimer)
		bk := this.getBucket(task.timerKey)
		bk.AddTask(task)
		return true
	} else {
		return false
	}
}

// 把任务插入桶
func (this *Timer) getBucket(key int64) *bucket {
	if this.lastKey > key {
		key = this.lastKey
	}
	if bk, ok := this.bucketList[key]; !ok {
		bk = PoolBucket.Get().(*bucket)
		bk.TimerKey = key
		this.bucketList[key] = bk
		return bk
	} else {
		return bk
	}
}

//----------------------------------------------------

// 一次任务 返回值(定时任务ID)
func (this *Timer) OnceTimer(delayTime time.Duration, callback func(...interface{}), params ...interface{}) uint64 {
	delay := delayTime.Milliseconds()
	if delay <= 0 {
		delay = this.TimerWheelAccuracy
	}
	return this.addTask(delay, 0, 0, TimerTask_one, callback, nil, params...)
}

// 循环任务 返回值(定时任务ID)
func (this *Timer) LoopTimer(intervalTime time.Duration, callback func(...interface{}), params ...interface{}) uint64 {
	looptime := intervalTime.Milliseconds()
	if looptime < this.TimerWheelAccuracy {
		looptime = this.TimerWheelAccuracy
	}
	return this.addTask(looptime, looptime, -1, TimerTask_loop, callback, nil, params...)
}

// 循环次数任务 返回值(定时任务ID)
func (this *Timer) LoopTimerByCount(intervalTime time.Duration, loopCount int, callback func(...interface{}), params ...interface{}) uint64 {
	looptime := intervalTime.Milliseconds()
	if looptime < this.TimerWheelAccuracy {
		looptime = this.TimerWheelAccuracy
	}
	return this.addTask(looptime, looptime, loopCount, TimerTask_loop, callback, nil, params...)
}

// 循环延时任务 返回值(定时任务ID)
func (this *Timer) LoopDelayTimer(delayTime time.Duration, intervalTime time.Duration, callback func(...interface{}), params ...interface{}) uint64 {
	delay := delayTime.Milliseconds()
	if delay <= 0 {
		delay = this.TimerWheelAccuracy
	}
	looptime := intervalTime.Milliseconds()
	if looptime < this.TimerWheelAccuracy {
		looptime = this.TimerWheelAccuracy
	}
	return this.addTask(delay, looptime, -1, TimerTask_loop, callback, nil, params...)
}

// 循环任务(毫秒) 返回值(定时任务ID)
func (this *Timer) LoopDelayTimerByCount(delayTime time.Duration, intervalTime time.Duration, loopCount int, callback func(...interface{}), params ...interface{}) uint64 {
	delay := delayTime.Milliseconds()
	if delay <= 0 {
		delay = this.TimerWheelAccuracy
	}
	looptime := intervalTime.Milliseconds()
	if looptime < this.TimerWheelAccuracy {
		looptime = this.TimerWheelAccuracy
	}
	return this.addTask(delay, looptime, loopCount, TimerTask_loop, callback, nil, params...)
}

// 删除定时任务(非线程安全)
func (this *Timer) DeleteTimerTask(timerTaskID uint64) {
	taskmd, ok := this.timerTasks[timerTaskID]
	if !ok {
		return
	}
	taskmd.isDelete = true
	delete(this.timerTasks, timerTaskID)
	if taskmd.endCallback != nil {
		taskmd.endCallback()
	}
	taskmd.callback = nil
	taskmd.params = nil
}

// 删除所有定时任务(非线程安全)
func (this *Timer) DeleteAllTimerTask() {
	this.timerTasks = make(map[uint64]*taskModel)
	for _, bk := range this.bucketList {
		for bk.Root != nil {
			tmp := bk.Root
			bk.Root = tmp.Next
			tmp.init()
			PoolListE.Put(tmp)
		}
		bk.Prev = nil
		bk.init()
		PoolBucket.Put(bk)
	}
	this.bucketList = make(map[int64]*bucket)
}

func (this *Timer) String() string {
	return fmt.Sprintln(this.timerTasks)
}
